package utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Date;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import sun.security.x509.AlgorithmId;
import sun.security.x509.CertificateAlgorithmId;
import sun.security.x509.CertificateIssuerName;
import sun.security.x509.CertificateSerialNumber;
import sun.security.x509.CertificateSubjectName;
import sun.security.x509.CertificateValidity;
import sun.security.x509.CertificateVersion;
import sun.security.x509.CertificateX509Key;
import sun.security.x509.X500Name;
import sun.security.x509.X509CertImpl;
import sun.security.x509.X509CertInfo;

public class CertGen {

	public static X509Certificate generateCertificate(String dn,
			String issuedTo, KeyPair pair, int days, String algorithm)
			throws GeneralSecurityException, IOException {
		PrivateKey privkey = pair.getPrivate();
		X509CertInfo info = new X509CertInfo();
		Date from = new Date();
		Date to = new Date(from.getTime() + days * 86400000l);
		CertificateValidity interval = new CertificateValidity(from, to);
		BigInteger sn = new BigInteger(64, new SecureRandom());
		X500Name owner = new X500Name("cn="+dn);
		X500Name issuer = new X500Name("cn="+issuedTo);

		info.set(X509CertInfo.VALIDITY, interval);
		info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn));
		info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(issuer));
		info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner));
		info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic()));
		info.set(X509CertInfo.VERSION, new CertificateVersion(
				CertificateVersion.V3));
		AlgorithmId algo = new AlgorithmId(AlgorithmId.sha1WithRSAEncryption_oid);
		info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo));

		// Sign the cert to identify the algorithm that's used.
		X509CertImpl cert = new X509CertImpl(info);
		cert.sign(privkey, algorithm);

		// Update the algorith, and resign.
		algo = (AlgorithmId) cert.get(X509CertImpl.SIG_ALG);
		info.set(CertificateAlgorithmId.NAME + "."
				+ CertificateAlgorithmId.ALGORITHM, algo);
		
		cert = new X509CertImpl(info);
		cert.sign(privkey, algorithm);
		return cert;
	}

	public static void encode(X509Certificate cert, String filename)
			throws UnsupportedEncodingException, IOException,
			CertificateEncodingException {
		final FileOutputStream os = new FileOutputStream(filename+".der");

		os.write("-----BEGIN CERTIFICATE-----\n".getBytes("US-ASCII"));

		BASE64Encoder encoder = new BASE64Encoder();
		String encoded = encoder.encode(cert.getEncoded());

		os.write(encoded.getBytes());
		os.write("\n-----END CERTIFICATE-----\n".getBytes("US-ASCII"));
		os.close();
	}
	
	public static ByteArrayOutputStream encode(X509Certificate cert)
			throws UnsupportedEncodingException, IOException,
			CertificateEncodingException {
		final ByteArrayOutputStream os = new ByteArrayOutputStream();

		os.write("-----BEGIN CERTIFICATE-----\n".getBytes("US-ASCII"));

		BASE64Encoder encoder = new BASE64Encoder();
		String encoded = encoder.encode(cert.getEncoded());

		os.write(encoded.getBytes());
		os.write("\n-----END CERTIFICATE-----\n".getBytes("US-ASCII"));
		os.close();
		return os;
	}
	
	public static String getString(X509Certificate cert)
			throws UnsupportedEncodingException, IOException,
			CertificateEncodingException {
		StringBuilder result = new StringBuilder();
		result.append("-----BEGIN CERTIFICATE-----\n");

		BASE64Encoder encoder = new BASE64Encoder();
		String encoded = encoder.encode(cert.getEncoded());

		result.append(encoded);
		result.append("\n-----END CERTIFICATE-----\n");

		return result.toString();
	}

	public static X509Certificate getCertifcate(String cert)
			throws IOException, CertificateException {

		cert = cert
				.replace("-----BEGIN CERTIFICATE-----", "");
		cert = cert.replace("-----END CERTIFICATE-----", "");
		
		BASE64Decoder decoder = new BASE64Decoder();
		byte[] decodedBytes = decoder.decodeBuffer(cert);

		InputStream in = new ByteArrayInputStream(decodedBytes);

		CertificateFactory factory = CertificateFactory.getInstance("X.509");
		X509Certificate certificate = (X509Certificate) factory
				.generateCertificate(in);

		return certificate;
	}

}
